// SLIC.h: interface for the SLIC class.
//===========================================================================
// This code implements the superpixel method described in:
//
// Radhakrishna Achanta, Appu Shaji, Kevin Smith, Aurelien Lucchi, Pascal Fua, and Sabine Susstrunk,
// "SLIC Superpixels",
// EPFL Technical Report no. 149300, June 2010.
//===========================================================================
//	Copyright (c) 2012 Radhakrishna Achanta [EPFL]. All rights reserved.
//===========================================================================
//////////////////////////////////////////////////////////////////////

#if !defined(_SLIC_H_INCLUDED_)
#define _SLIC_H_INCLUDED_


#include <vector>
#include <string>
#include <algorithm>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;


class LcColorMoment
{
public:
	vector <double> Z;
	//the array z is 1 R G B RR GG BB RG RB GB
	LcColorMoment(){ Z.resize(10); for(int i = 0 ; i <10;i++) Z[i] = 0;}	

	void Set(const double & z1,const double &  z2,const double &  z3);
	void Add(const double & z1,const double &  z2,const double &  z3);


	void Add(LcColorMoment & m)	{for(int i=0;i<10;i++) Z[i]+=m.Z[i];}

	void Add(double w, LcColorMoment & m)	{for(int i=0;i<10;i++) Z[i]+=(m.Z[i]*w);}

	void Set(const double * vec_z)
			{	Set(*vec_z,*(vec_z+1),*(vec_z+2));	}//unsafty if vec_z is NULL
	double & operator[](const int & index){return Z[index];}

	void get_Color_Kernel(double * mu,double * p);

	void ColorOutput(FILE * out=NULL){ if(out==NULL){ for(int i=0;i<4;i++) printf("%.3lf\t",Z[i]/(Z[0]+1e-6));printf("\n");}
								  else for(int i=0;i<4;i++) fprintf(out,"%lf\t",Z[i]/(Z[0]+1e-6));}

	void Output(FILE * out){for(int i=0;i<10;i++) fprintf(out,"%lf\t",Z[i]);}

	void Output(){for(int i=0;i<10;i++) printf("%lf\t",Z[i]);}

	void Zero(){for(int i=0;i<10;i++) Z[i] = 0;}

};


class LcCoorMoment
{
public:
	vector <double> X;
	//the array x is 1 x y xx yy xy
	LcCoorMoment(){ X.resize(6); for(int i = 0 ; i <6;i++) X[i] = 0;}	

	void Set(const double & x, const double & y);
	void Add(const double & x, const double & y);

	void Add(LcCoorMoment & m){ for(int i = 0;i<6;i++) X[i]+=m.X[i];}
	void Add(double w,LcCoorMoment & m){ for(int i = 0;i<6;i++) X[i]+=(w * m.X[i]);}

	double & operator[](const int & index){return X[index];}

	void Output(FILE * out){for(int i = 0;i<6;i++) fprintf(out,"%lf\t",X[i]);}

	void Zero(){for(int i = 0;i<6;i++) X[i] = 0;}

	double GetGX() { return X[1]/X[0];};
	double GetGY() { return X[2]/X[0];};

	void get_Inertia_Matrix(double * matrix);

};

class SLIC  
{
public:
	SLIC();
	
	virtual ~SLIC();

	//============================================================================
	//Cheng Li
		void GetMomentOnSuperpixel(
			int						sz,
			int*				    klabels,
			int					numlabels,
			LcColorMoment*&	color_moment,
			unsigned char *		C3_image);
		void GetCoorMomentOnSuperpixel(
			int						width,
			int						height,
			int*				    klabels,
			int					numlabels,
			LcCoorMoment*&	coor_moment);

	//============================================================================
	// Superpixel segmentation for a given step size (superpixel size ~= step*step)
	//============================================================================
        void DoSuperpixelSegmentation_ForGivenSuperpixelSize(
        const unsigned int*                            ubuff,//Each 32 bit unsigned int contains ARGB pixel values.
		const int					width,
		const int					height,
		int*&						klabels,
		int&						numlabels,
                const int&					superpixelsize,
                const double&                                   compactness);
	//============================================================================
	// Superpixel segmentation for a given number of superpixels
	//============================================================================
        void DoSuperpixelSegmentation_ForGivenNumberOfSuperpixels(
        const unsigned int*                             ubuff,
		const int					width,
		const int					height,
		int*&						klabels,
		int&						numlabels,
                const int&					K,//required number of superpixels
                const double&                                   compactness);//10-20 is a good value for CIELAB space
	//============================================================================
	// Supervoxel segmentation for a given step size (supervoxel size ~= step*step*step)
	//============================================================================
	void DoSupervoxelSegmentation(
		unsigned int**&		ubuffvec,
		const int&					width,
		const int&					height,
		const int&					depth,
		int**&						klabels,
		int&						numlabels,
                const int&					supervoxelsize,
                const double&                                   compactness);
	//============================================================================
	// Save superpixel labels in a text file in raster scan order
	//============================================================================
	void SaveSuperpixelLabels(
		const int*&					labels,
		const int&					width,
		const int&					height,
		const string&				filename,
		const string&				path);
	//============================================================================
	// Save supervoxel labels in a text file in raster scan, depth order
	//============================================================================
	void SaveSupervoxelLabels(
		const int**&				labels,
		const int&					width,
		const int&					height,
		const int&					depth,
		const string&				filename,
		const string&				path);
	//============================================================================
	// Function to draw boundaries around superpixels of a given 'color'.
	// Can also be used to draw boundaries around supervoxels, i.e layer by layer.
	//============================================================================
	void DrawContoursAroundSegments(
		unsigned int*&				segmentedImage,
		int*&						labels,
		const int&					width,
		const int&					height,
		const unsigned int&			color );
	void DrawBiContoursAroundSegments(
		int*&			ubuff,
		int*&					labels,
		const int&				width,
		const int&				height);

private:
	//============================================================================
	// The main SLIC algorithm for generating superpixels
	//============================================================================
	void PerformSuperpixelSLIC(
		vector<double>&				kseedsl,
		vector<double>&				kseedsa,
		vector<double>&				kseedsb,
		vector<double>&				kseedsx,
		vector<double>&				kseedsy,
		int*&						klabels,
		const int&					STEP,
                const vector<double>&		edgemag,
		const double&				m = 10.0);
	//============================================================================
	// The main SLIC algorithm for generating supervoxels
	//============================================================================
	void PerformSupervoxelSLIC(
		vector<double>&				kseedsl,
		vector<double>&				kseedsa,
		vector<double>&				kseedsb,
		vector<double>&				kseedsx,
		vector<double>&				kseedsy,
		vector<double>&				kseedsz,
		int**&						klabels,
		const int&					STEP,
		const double&				compactness);
	//============================================================================
	// Pick seeds for superpixels when step size of superpixels is given.
	//============================================================================
	void GetLABXYSeeds_ForGivenStepSize(
		vector<double>&				kseedsl,
		vector<double>&				kseedsa,
		vector<double>&				kseedsb,
		vector<double>&				kseedsx,
		vector<double>&				kseedsy,
		const int&					STEP,
		const bool&					perturbseeds,
		const vector<double>&		edgemag);
	//============================================================================
	// Pick seeds for supervoxels
	//============================================================================
	void GetKValues_LABXYZ(
		vector<double>&				kseedsl,
		vector<double>&				kseedsa,
		vector<double>&				kseedsb,
		vector<double>&				kseedsx,
		vector<double>&				kseedsy,
		vector<double>&				kseedsz,
		const int&					STEP);
	//============================================================================
	// Move the superpixel seeds to low gradient positions to avoid putting seeds
	// at region boundaries.
	//============================================================================
	void PerturbSeeds(
		vector<double>&				kseedsl,
		vector<double>&				kseedsa,
		vector<double>&				kseedsb,
		vector<double>&				kseedsx,
		vector<double>&				kseedsy,
		const vector<double>&		edges);
	//============================================================================
	// Detect color edges, to help PerturbSeeds()
	//============================================================================
	void DetectLabEdges(
		const double*				lvec,
		const double*				avec,
		const double*				bvec,
		const int&					width,
		const int&					height,
		vector<double>&				edges);
	//============================================================================
	// sRGB to XYZ conversion; helper for RGB2LAB()
	//============================================================================
	void RGB2XYZ(
		const int&					sR,
		const int&					sG,
		const int&					sB,
		double&						X,
		double&						Y,
		double&						Z);
	//============================================================================
	// sRGB to CIELAB conversion (uses RGB2XYZ function)
	//============================================================================
	void RGB2LAB(
		const int&					sR,
		const int&					sG,
		const int&					sB,
		double&						lval,
		double&						aval,
		double&						bval);
	//============================================================================
	// sRGB to CIELAB conversion for 2-D images
	//============================================================================
	void DoRGBtoLABConversion(
		const unsigned int*&		ubuff,
		double*&					lvec,
		double*&					avec,
		double*&					bvec);
	//============================================================================
	// sRGB to CIELAB conversion for 3-D volumes
	//============================================================================
	void DoRGBtoLABConversion(
		unsigned int**&				ubuff,
		double**&					lvec,
		double**&					avec,
		double**&					bvec);
	//============================================================================
	// Post-processing of SLIC segmentation, to avoid stray labels.
	//============================================================================
	void EnforceLabelConnectivity(
		const int*					labels,
		const int					width,
		const int					height,
		int*&						nlabels,//input labels that need to be corrected to remove stray labels
		int&						numlabels,//the number of labels changes in the end if segments are removed
		const int&					K); //the number of superpixels desired by the user
	//============================================================================
	// Post-processing of SLIC supervoxel segmentation, to avoid stray labels.
	//============================================================================
	void EnforceSupervoxelLabelConnectivity(
		int**&						labels,//input - previous labels, output - new labels
		const int&					width,
		const int&					height,
		const int&					depth,
		int&						numlabels,
		const int&					STEP);

private:
	int										m_width;
	int										m_height;
	int										m_depth;

	double*									m_lvec;
	double*									m_avec;
	double*									m_bvec;

	double**								m_lvecvec;
	double**								m_avecvec;
	double**								m_bvecvec;
};


extern void image2superpixel(Mat & src,  Mat & label_mat, Mat & m_dist,LcColorMoment*& color_moment,LcCoorMoment*& coor_moment,int & n_pixel,int k = 500);

#endif // !defined(_SLIC_H_INCLUDED_)
